﻿#region Using namespaces...
using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using System.Text;
using System.Xml.Linq;
using Microsoft.SharePoint;
using Microsoft.SharePoint.Taxonomy;
using TaxonomyImportLibrary.Taxonomy.Interfaces;
using TaxonomyImportLibrary.Taxonomy.Views;
using TaxonomyImportLibrary.Support;
using TaxonomyImportLibrary.Library;
#endregion


namespace TaxonomyImportLibrary.Taxonomy
{
	/// <summary>
	/// TaxonomyXmlImportManager class
	/// </summary>
	public class TaxonomyXmlImportManager
	{
		#region Singleton Members
		/// <summary>
		/// Singleton member instance
		/// </summary>
		static private volatile TaxonomyXmlImportManager _instance;
		/// <summary>
		/// Singleton sinchronization object
		/// </summary>
		static private object _sync = new object();
		#endregion

		#region Singleton Properties
		/// <summary>
		/// Gets Singleton instance
		/// </summary>
		static public TaxonomyXmlImportManager Instance
		{
			get
			{
				if (_instance == null)
				{
					lock (_sync)
					{
						if (_instance == null)
							_instance = new TaxonomyXmlImportManager();
					}
				}

				return _instance;
			}
		}
		#endregion

		#region Constructors
		/// <summary>
		/// Default constructor
		/// </summary>
		private TaxonomyXmlImportManager() { }
		#endregion

		#region Properties
		/// <summary>
		/// Gets/Sets XmlSource
		/// </summary>
		public XDocument XmlSource { get; set; }
		/// <summary>
		/// Gets/Sets Definition
		/// </summary>
		public TaxonomyDefinitionView Definition { get; set; }
		/// <summary>
		/// Gets/Sets SharePointSite
		/// </summary>
		private SPSite SharePointSite { get; set; }
		/// <summary>
		/// Gets/Sets Session
		/// </summary>
		private TaxonomySession Session { get; set; }
		/// <summary>
		/// Gets/Sets DefaultStore
		/// </summary>
		private TermStore DefaultStore { get; set; }
		#endregion

		#region Public Methods
		/// <summary>
		/// Handles building of the SharePoint term store from xml
		/// </summary>
		/// <param name="source"></param>
		/// <param name="sourceType"></param>
		public void BuildTermStore()
		{
			// Process
			if (!string.IsNullOrEmpty(Definition.SiteUrl))
			{
				using (SharePointSite = new SPSite(Definition.SiteUrl))
				{
					if (!(SharePointSite == null))
					{
						// Ensure term store feature is activated
						EnsureTermStoreFeatureActivated();

						// Set taxonomy session
						Session = new TaxonomySession(SharePointSite);

						if (!(Session == null))
						{
							// Set default store
							DefaultStore = Session.DefaultSiteCollectionTermStore;

							// Build
							if (!(DefaultStore == null))
								Build();
						}
					}
				}
			}
		}

		/// <summary>
		/// Loads SharePoint 2010 Taxonomy definitions from XML
		/// </summary>
		/// <param name="source"></param>
		/// <returns></returns>
		public TaxonomyDefinitionView LoadTaxonomyFromXml(string source, XmlSourceTypes sourceType)
		{
			// Set xml document source
			XmlSource = sourceType == XmlSourceTypes.FileSource ?
					XDocument.Load(source, LoadOptions.PreserveWhitespace)
					: XDocument.Parse(source, LoadOptions.PreserveWhitespace);

			// Pre-allocate return value
			TaxonomyDefinitionView definition = new TaxonomyDefinitionView() 
				{ 
					SiteUrl = XmlSource.Root.Attribute("site").Value
				};

			// Load XML to object hierarchy
			definition.ChildGroups = LoadGroups(definition);

			// Set definition views
			Definition = definition;

			return definition;
		}
		#endregion

		#region Utility Methods
		/// <summary>
		/// Ensures term store feature is activated for the current farm
		/// </summary>
		/// <param name="properties"></param>
		private void EnsureTermStoreFeatureActivated()
		{
			// Activate term store feature
			FeatureActivator.Instance.ActivateSiteFeature(SharePointSite, new Guid(LibraryConstants.TERM_STORE_ID));
		}

		/// <summary>
		/// Handles building of taxonomy definition on SharePoint
		/// </summary>
		private void Build()
		{
			// Process
			if (!(Definition == null))
			{
				foreach (IGroupView groupDefinition in Definition.ChildGroups)
				{
					if (!string.IsNullOrEmpty(groupDefinition.Name))
					{
						if (DefaultStore.Groups.SingleOrDefault(x => x.Name == groupDefinition.Name) == null)
						{
							// Load group
							Group group = DefaultStore.CreateGroup(groupDefinition.Name);
							group.Description = groupDefinition.Description;
							if (groupDefinition.Managers.Count > 0)
							{
								foreach (IUserIdentityView ident in groupDefinition.Managers)
									group.AddGroupManager(ident.Identity);
							}

							// Load term sets
							LoadGroupTermSets(group, groupDefinition);
						}

						// Commit changes
						DefaultStore.CommitAll();
					}
				}
			}
		}

		/// <summary>
		/// Handles building of term sets for the group definition
		/// </summary>
		/// <param name="group"></param>
		/// <param name="groupDefinition"></param>
		private void LoadGroupTermSets(Group group, IGroupView groupDefinition)
		{
			// Process
			foreach (ITermSetView termsetDefinition in groupDefinition.ChildTermSets)
			{
				if (!string.IsNullOrEmpty(termsetDefinition.Name))
				{
					// Load term set
					if (group.TermSets.SingleOrDefault(x => x.Name == termsetDefinition.Name) == null)
					{
						TermSet termSet = group.CreateTermSet(termsetDefinition.Name);
						termSet.Description = termsetDefinition.Description;
						termSet.IsAvailableForTagging = termsetDefinition.AvailableForTagging;
						termSet.Contact = termsetDefinition.ContactEmail;
						termSet.Owner = termsetDefinition.Owner;
						if (termsetDefinition.Stakeholders.Count > 0)
						{
							foreach (IUserIdentityView identView in termsetDefinition.Stakeholders)
								termSet.AddStakeholder(identView.Identity);
						}
						

						// Load terms
						if (termsetDefinition.ChildTerms.Count > 0)
						{
							foreach (ITermView termDefinition in termsetDefinition.ChildTerms)
							{
								if (!string.IsNullOrEmpty(termDefinition.DefaultLabel))
								{
									Term term = termSet.CreateTerm(termDefinition.DefaultLabel, CultureInfo.CurrentCulture.LCID);
									term.SetDescription(termDefinition.Description, CultureInfo.CurrentCulture.LCID);
									term.Owner = termSet.Owner;
									AttachTermLabels(termDefinition, term);

									// Load children
									if (termDefinition.ChildTerms.Count > 0)
										LoadChildTerms(term, termDefinition.ChildTerms, termSet.Owner);
								}
							}
						}
					}
				}
			}
		}

		/// <summary>
		/// Handles loading of child terms recursively
		/// </summary>
		/// <param name="term"></param>
		/// <param name="list"></param>
		private void LoadChildTerms(Term term, List<ITermView> list, string owner)
		{
			foreach (ITermView termDefinition in list)
			{
				if (!string.IsNullOrEmpty(termDefinition.DefaultLabel))
				{
					Term childTerm = term.CreateTerm(termDefinition.DefaultLabel, CultureInfo.CurrentCulture.LCID);
					childTerm.SetDescription(termDefinition.Description, CultureInfo.CurrentCulture.LCID);
					childTerm.Owner = owner;
					AttachTermLabels(termDefinition, childTerm);

					// Load children
					if (termDefinition.ChildTerms.Count > 0)
						LoadChildTerms(childTerm, termDefinition.ChildTerms, owner);
				}
			}
		}

		/// <summary>
		/// Handles attaching of additional term labels
		/// </summary>
		/// <param name="termDefinition"></param>
		/// <param name="term"></param>
		private static void AttachTermLabels(ITermView termDefinition, Term term)
		{
			if (termDefinition.OtherLabels.Count > 0)
			{
				foreach (string label in termDefinition.OtherLabels)
					term.CreateLabel(label, CultureInfo.CurrentCulture.LCID, false);
			}
		}

		/// <summary>
		/// Loads all taxonomy groups
		/// </summary>
		/// <param name="definition"></param>
		private List<IGroupView> LoadGroups(TaxonomyDefinitionView definition)
		{
			// Retrieve groups from source
			return (from g in XmlSource.Root.Elements("group")
					select new GroupView()
					{
						Name = g.Attribute("name").Value,
						Description = g.Attribute("description").Value,
						Managers = (from m in g.Elements("managers")
									select new UserIdentityView()
									{
										Identity = m.Element("manager").Attribute("identity").Value
									}).Cast<IUserIdentityView>().ToList(),
						Contributors = (from c in g.Elements("contributors")
										select new UserIdentityView()
										{
											Identity = c.Element("contributor").Attribute("identity").Value
										}).Cast<IUserIdentityView>().ToList(),
						ChildTermSets = (from ts in g.Elements("termset")
										 select new TermSetView()
										 {
											 Name = ts.Attribute("name").Value,
											 Description = ts.Attribute("description").Value,
											 AvailableForTagging = ts.Attribute("availablefortagging").Value.Equals("true") ? true : false,
											 Stakeholders = (from s in ts.Elements("stakeholders")
															 select new UserIdentityView()
															 {
																 Identity = s.Element("stakeholder").Attribute("identity").Value
															 }).Cast<IUserIdentityView>().ToList(),
											 ContactEmail = ts.Element("contact").Attribute("email").Value,
											 Owner = ts.Element("owner").Attribute("identity").Value,
											 ChildTerms = LoadTerms((from ct in ts.Elements("term") select ct).ToList())
											 
										 }).Cast<ITermSetView>().ToList()

					}).Cast<IGroupView>().ToList();
		}

		/// <summary>
		/// Handles loading of terms recursively
		/// </summary>
		/// <param name="list"></param>
		/// <returns></returns>
		private List<ITermView> LoadTerms(List<XElement> list)
		{
			return (from t in list
					select new TermView()
					{
						DefaultLabel = t.Attribute("defaultlabel").Value,
						Description = t.Attribute("description").Value,
						Language = t.Attribute("language").Value,
						AvailableForTagging = t.Attribute("availablefortagging").Value.Equals("true") ? true : false,
						OtherLabels = GetAdditionalLabels(t),
						ChildTerms = LoadTerms((from ct in t.Elements("term") select ct).ToList())
					}).Cast<ITermView>().ToList();
		}

		/// <summary>
		/// Handles loading of additional term labels
		/// </summary>
		/// <param name="element"></param>
		/// <returns></returns>
		private List<string> GetAdditionalLabels(XElement element)
		{
			// Pre-allocate return value
			List<string> retVal = new List<string>();
			if (!(element.Element("labels") == null))
				retVal = (from x in element.Element("labels").Descendants("label")
								   where !string.IsNullOrEmpty(x.Attribute("name").Value)
								   select x.Attribute("name").Value).ToList();

			return retVal;
		}
		#endregion
	}
}
